﻿<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true"
    CodeFile="Default.aspx.cs" Inherits="_Default" %>

<asp:Content ID="HeaderContent" runat="server" ContentPlaceHolderID="HeadContent">
</asp:Content>
<asp:Content ID="BodyContent" runat="server" ContentPlaceHolderID="MainContent">
    <style>
        .ui-autocomplete
        {
            max-height: 100px;
            overflow-y: auto; /* prevent horizontal scrollbar */
            overflow-x: hidden; /* add padding to account for vertical scrollbar */
            padding-right: 20px;
        }
        /* IE 6 doesn't support max-height
	 * we use height instead, but this forces the menu to always be this tall
	 */
        * html .ui-autocomplete
        {
            height: 100px;
        }
    </style>
    <script type="text/javascript">
        $(document).ready(function () {
            //Input for testing purposes
            $("#inp").smartautocomplete({
                getDataFunc: getData,
                pageSize: 15,
                autoFocus: true
            });

            //Input for testing purposes
            $("#inp2").smartautocomplete({
                getDataFunc: getData,
                pageSize: 15,
                autoFocus: true
            });
        });

        //Function the SA plugin called when data is needed. 
        var getData = function (input, pageIndex, pageSize, callback) {
            //In this example I use a WebMethod, but you can call anything from a local source to any web service.
            PageMethods.GetData(input, pageIndex, pageSize, function (response) {
                if (response) {
                    //Data is assumed to be received in a {label: , value: , ...} form, as needed by jqueryUI autocomplete. Of course, if you change the _renderItem function, you are free to modify this as you want
                    response = $.map(response, function (item) {
                        return {
                            label: item,
                            value: item
                        }
                    });
                    callback(response);
                }
                else callback();
            });
        };

        //We define our jQuery plugin
        (function ($) {
            var methods = {
                init: function (options) {
                    return this.each(function () {
                        if ($(this).data('SA'))
                            return resetData();

                        var parent = $(this);
                        var autocomplete = null;
                        var menu = null;
                        var focusedItem = null;
                        var pageIndex = -1;
                        var lastScrollTop = 0;
                        var selectedIndex = 0;
                        var lastSelectedIndex = -1;
                        var data = new Array();
                        var ignoreFocus = false;
                        var loading = false;
                        var startedLoadingFromScroll = false;

                        var startSearching = function (si) {
                            lastScrollTop = autocomplete.scrollTop();
                            if (!si) {
                                var activeElement = autocomplete.find(".ui-state-hover");
                                if (activeElement.length) {
                                    activeElement = activeElement.parent();
                                    selectedIndex = data.length ? activeElement.parent().children().index(activeElement) : 0;
                                }
                            }
                            else selectedIndex = si;
                            if (!selectedIndex)
                                lastScrollTop = 0;
                            parent.autocomplete('search');
                        };

                        //Resets the current search
                        var resetData = function () {
                            data = new Array();
                            pageIndex = -1;
                            selectedIndex = 0;
                            lastSelectedIndex = -1;
                            focusedItem = null;
                            loading = false;
                            startedLoadingFromScroll = false;
                            lastScrollTop = 0;
                            autocomplete.scrollTop(0);
                            parent.removeAttr('sa-value').autocomplete('option', 'disabled', false);
                            parent.data().autocomplete.term = null;
                        };

                        //JS event cancelling logic
                        var stopEvent = function (e) {
                            if (e.stopPropagation)
                                e.stopPropagation();
                            else e.cancelBubble = true;
                            if (e.preventDefault)
                                e.preventDefault();
                            else e.returnValue = false;
                            return false;
                        };

                        //We overwrite the source logic to call our getDataFunc function
                        options.source = function (request, response) {
                            //We won't start another search while a search is still in progress
                            if (loading)
                                return;

                            //We verify that options.getDataFunc exists and it is a function
                            if (options.getDataFunc && typeof(options.getDataFunc) === "function") {
                                //If the selected item has not changed, we return
                                if (lastSelectedIndex == selectedIndex)
                                    return;

                                lastSelectedIndex = selectedIndex;

                                //We get the term we are searching for
                                var term = parent.attr('sa-value');
                                if (term) {
                                    loading = true;

                                    //We call the function to get the data
                                    options.getDataFunc(term, pageIndex + 1, options.pageSize, function (r) {
                                        loading = false;
                                        if (r) {
                                            //We allow scrolling
                                            if (!r.length)
                                                startedLoadingFromScroll = false;

                                            //If data already exists, we add our new data to the existing data
                                            if (data.length)
                                                for (var i = 0; i < r.length; i++)
                                                    data.push(r[i]);
                                            else data = r;
                                            response(data);

                                            //We scroll to the last position
                                            autocomplete.scrollTop(lastScrollTop);

                                            //We increment the current page index
                                            pageIndex++;
                                        }
                                    });
                                }
                            }
                        };

                        $(this).bind('autocompletecreate', function () {
                            //We get the elements created by JqueryUI Autocomplete
                            autocomplete = $(this).autocomplete('widget');
                            menu = parent.data().autocomplete.menu.element;

                            if (autocomplete.attr('sa-scroll') != 'on') {
                                //We create the scrolling functionality to request new data when we arrived at the end of list
                                autocomplete.scroll(function (e) {
                                    if (loading)
                                        return stopEvent(e);
                                    if (startedLoadingFromScroll) {
                                        if ($.browser.msie || $.browser.mozilla)
                                            autocomplete.scrollTop(lastScrollTop);
                                        return stopEvent(e);
                                    }
                                    if (autocomplete[0].scrollHeight - autocomplete.scrollTop() <= autocomplete.outerHeight()) {
                                        startedLoadingFromScroll = true;
                                        startSearching(Math.max(autocomplete.find(".ui-menu-item").length - 1, 0));
                                    }
                                }).attr('sa-scroll', 'on');

                                //After releasing the mouse just after getting new data, we release the scrolling functionality
                                $(document).mousemove(function (e) {
                                    if (!e.buttons)
                                        startedLoadingFromScroll = false;
                                });
                            }
                            startSearching();
                        })
                        .autocomplete(options)
                        .bind('autocompletefocus', function (event, ui) {
                            focusedItem = ui.item;
                            if (ignoreFocus) {
                                ignoreFocus = false;
                                return;
                            }
                            //If we reached the last element in the list, we get new data
                            if (autocomplete.find(".ui-menu-item:last .ui-state-hover").length)
                                startSearching();
                        })
                        .keyup(function (e) {
                            if (e.keyCode == 27)
                                return resetData();
                            if (e.keyCode == 38 || e.keyCode == 40 || focusedItem && focusedItem.label == parent.val())
                                return;
                            if (parent.attr('sa-value') != parent.val())
                                resetData();
                            parent.attr('sa-value', parent.val());
                        })
                        .bind('autocompleteopen', function (event, ui) {
                            if (!selectedIndex) {
                                if (options.autoFocus) {
                                    ignoreFocus = true;
                                    menu.menu('activate', event, autocomplete.find(".ui-menu-item:first"));
                                }
                            }
                            else {
                                ignoreFocus = true;
                                menu.menu('activate', event, autocomplete.find(".ui-menu-item:eq(" + selectedIndex + ")"));
                            }
                        })
                        .bind('autocompleteclose', function (event, ui) {
                            //When the dropdown closes, we reset the current search
                            resetData();
                        }).data('SA', this);
                    });
                },
                destroy: function () {
                    $(this).removeData('SA').autocomplete('destroy');
                }
            };

            //We name our plugin smartautocomplete
            $.fn.smartautocomplete = function (method) {
                //Method calling logic as described in jQuery Plugin Authoring documentation: http://docs.jquery.com/Plugins/Authoring
                if (methods[method])
                    return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
                else if (typeof method === 'object' || !method)
                    return methods.init.apply(this, arguments);
                else $.error('Method ' + method + ' does not exist on jQuery.smartautocomplete');
            };
        })(jQuery);

    </script>
    <asp:ScriptManager ID="scriptManager" runat="server" EnablePageMethods="true">
    </asp:ScriptManager>
    <h2>
        <input id="inp" type="text" style="width: 350px;" />
        <input id="inp2" type="text" style="width: 350px;" />
    </h2>
</asp:Content>